import {dynamicObj} from "../types";
import {isNil} from 'ramda'

class SingletonManage {
    data: object
    promiseInstance: object
    option: object
    constructor(option) {
        // todo 检查option结构
        this.data = {}
        this.promiseInstance = {}
        this.option = option
    }
    async get(name) {
        // 内存有值返回值
        if(this.data[name]) return this.data[name]
        // 已发起请求需等待
        if(this.promiseInstance[name]) {
            await this.promiseInstance[name]
            return this.data[name]
        }
        if(!this.promiseInstance[name]) {
            if(!this.option[name]) return null
            this.promiseInstance[name] = this.option[name]()
            this.data[name] = await this.promiseInstance[name]
            this.promiseInstance[name] = null
            return this.data[name]
        }
    }
    getSync(name) {
        // 内存有值返回值
        if(this.data[name]) return this.data[name]
        else {
            this.data[name] = this.option[name]()
            return this.data[name]
        }
    }
    async set(name, func) {
        // todo 命名冲突校验
        if(Object.prototype.toString.call(func) === '[object Function]') {
            this.option[name] = func
        }else if(Object.prototype.toString.call(func) === '[object AsyncFunction]'){
            if(!this.promiseInstance[name]) {
                this.promiseInstance[name] = func()
                this.data[name] = await this.promiseInstance[name]
                this.promiseInstance[name] = null
            }
        }
    }
    async refresh(name) {
        this.data[name] = null
        return await this.get(name)
    }
}

/**
 * 创建单例仓库
 * @param option 键值是getter函数，可以是async函数
 */
export function createSM(option) {
    const instance = new SingletonManage(option)
    const handler = {
        get: (target: dynamicObj, key: string): any => {
            if(!isNil(target[key])) {
                return target[key]
            }else {
                return target.getSync(key)
            }
        }
    }
    // 支持直接取值
    return new Proxy(instance, handler)
}

export default {
    createSM
}
